﻿/**
  ******************************************************************************
  * @file log.h
  * @author  Archer
  * @version
  * @date 2019/11/11
  * @brief
  ******************************************************************************
  * @attention
  *
  *
  * Copyright (c) 2011-2020 by Archer <tokisaki_kurumi@foxmail.com>
        All rights reserved.
  ******************************************************************************
  */
#ifndef _LOG_LOG_H_
#define _LOG_LOG_H_

#include <iostream>

#define LOG_USE_P7_FMT 1
#define LOG_USE_STDIO 2
#define LOG_USE_NULL 0

#ifndef CONFIG_ENABLE_LOG
#define CONFIG_ENABLE_LOG LOG_USE_P7_FMT
#endif

#if CONFIG_ENABLE_LOG == LOG_USE_P7_FMT

#include "P7_Trace.h"


extern IP7_Trace *pConsoleTrace;
extern IP7_Trace *pFileTxtTrace;
extern IP7_Trace *pCustomTrace;

#define DEFAULT_TRACE (pConsoleTrace)

#define LOG_CRITICAL(...)                       \
  do {                                          \
    pCustomTrace->P7_CRITICAL(0, __VA_ARGS__);  \
    pFileTxtTrace->P7_CRITICAL(0, __VA_ARGS__);  \
    DEFAULT_TRACE->P7_CRITICAL(0, __VA_ARGS__); \
  } while (0)

#define LOG_ERR(...)                            \
  do {                                       \
    pCustomTrace->P7_ERROR(0, __VA_ARGS__);  \
    pFileTxtTrace->P7_ERROR(0, __VA_ARGS__);     \
    DEFAULT_TRACE->P7_ERROR(0, __VA_ARGS__);    \
  } while (0)

#define LOG_WARN(...)                           \
  do {                                         \
    pCustomTrace->P7_WARNING(0, __VA_ARGS__);  \
    pFileTxtTrace->P7_WARNING(0, __VA_ARGS__);   \
    DEFAULT_TRACE->P7_WARNING(0, __VA_ARGS__);  \
  } while (0)

#define LOG_INFO(...)                           \
  do {                                      \
    pCustomTrace->P7_INFO(0, __VA_ARGS__);  \
    pFileTxtTrace->P7_INFO(0, __VA_ARGS__);      \
    DEFAULT_TRACE->P7_INFO(0, __VA_ARGS__);     \
  } while (0)

#define LOG_DEBUG(...)                          \
  do {                                       \
    pCustomTrace->P7_DEBUG(0, __VA_ARGS__);  \
    pFileTxtTrace->P7_DEBUG(0, __VA_ARGS__);     \
    DEFAULT_TRACE->P7_DEBUG(0, __VA_ARGS__);    \
  } while (0)

#define LOG_TRACE(...)                          \
  do {                                       \
    pCustomTrace->P7_TRACE(0, __VA_ARGS__);  \
    pCustomTrace->P7_TRACE(0, __VA_ARGS__);     \
    DEFAULT_TRACE->P7_TRACE(0, __VA_ARGS__);    \
  } while (0)

#define _CHECK2(expr, msg)                                                     \
  do {                                                                         \
    if (!(expr)) {                                                             \
      std::string msg_fatal = fmt::format("Check failed : {} {}", #expr, msg); \
      CRITICAL(msg_fatal.c_str());                                             \
      P7_Exceptional_Flush();                                                  \
      throw std::runtime_error(msg_fatal);                                     \
    }                                                                          \
  } while (0)

#elif CONFIG_ENABLE_LOG == LOG_USE_STDIO

#include <sstream>

#ifndef LOG_PREFIX
#define LOG_PREFIX
#endif
#define LOG_CRITICAL(...)                    \
  do {                                   \
    printf("[CRITICAL]" LOG_PREFIX " "); \
    printf(__VA_ARGS__);                 \
    printf("\n");                        \
  } while (0)

#define LOG_ERR(...)                    \
  do {                              \
    printf("[ERR]" LOG_PREFIX " "); \
    printf(__VA_ARGS__);            \
    printf("\n");                   \
  } while (0)

#define LOG_WARN(...)                    \
  do {                               \
    printf("[WARN]" LOG_PREFIX " "); \
    printf(__VA_ARGS__);             \
    printf("\n");                    \
  } while (0)

#define LOG_INFO(...)                    \
  do {                               \
    printf("[INFO]" LOG_PREFIX " "); \
    printf(__VA_ARGS__);             \
    printf("\n");                    \
  } while (0)

#define LOG_DEBUG(...)                    \
  do {                                \
    printf("[DEBUG]" LOG_PREFIX " "); \
    printf(__VA_ARGS__);              \
    printf("\n");                     \
  } while (0)

#define LOG_TRACE(...)                    \
  do {                                \
    printf("[TRACE]" LOG_PREFIX " "); \
    printf(__VA_ARGS__);              \
    printf("\n");                     \
  } while (0)

#define _CHECK2(expr, msg)                                                  \
  do {                                                                      \
    if (!(expr)) {                                                          \
      std::stringstream ss_msg_fatal;                                       \
      ss_msg_fatal << "Check failed: " << #expr << " @ " << __FILE__ << ":" \
                   << __LINE__ << "\n"                                      \
                   << msg;                                                  \
      throw std::runtime_error(ss_msg_fatal.str());                         \
    }                                                                       \
  } while (0)

#else /* CONFIG_ENABLE_LOG  */
#warning "Log disabled."

#define LOG_CRITICAL(...)
#define LOG_ERR(...)
#define LOG_WARN(...)
#define LOG_INFO(...)
#define LOG_DEBUG(...)
#define LOG_TRACE(...)

#define _CHECK2(expr, msg)

#endif /* CONFIG_ENABLE_LOG  */

#define OVERLOADED_MACRO(M, ...) _OVR(M, _COUNT_ARGS(__VA_ARGS__))(__VA_ARGS__)
#define _OVR(macroName, number_of_args) _OVR_EXPAND(macroName, number_of_args)
#define _OVR_EXPAND(macroName, number_of_args) macroName##number_of_args

#define _COUNT_ARGS(...) \
  _ARG_PATTERN_MATCH(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1)
#define _ARG_PATTERN_MATCH(_1, _2, _3, _4, _5, _6, _7, _8, _9, N, ...) N

#define _CHECK1(expr) _CHECK2(expr, "")

#define _CHECK(...) OVERLOADED_MACRO(_CHECK, __VA_ARGS__)

#define _CHECK_EQ3(lhs, rhs, msg) _CHECK2((lhs) == (rhs), msg)
#define _CHECK_NE3(lhs, rhs, msg) _CHECK2((lhs) != (rhs), msg)
#define _CHECK_LE3(lhs, rhs, msg) _CHECK2((lhs) <= (rhs), msg)
#define _CHECK_LT3(lhs, rhs, msg) _CHECK2((lhs) < (rhs), msg)
#define _CHECK_GE3(lhs, rhs, msg) _CHECK2((lhs) >= (rhs), msg)
#define _CHECK_GT3(lhs, rhs, msg) _CHECK2((lhs) > (rhs), msg)

#define _CHECK_EQ2(lhs, rhs) _CHECK_EQ3(lhs, rhs, "")
#define _CHECK_NE2(lhs, rhs) _CHECK_NE3(lhs, rhs, "")
#define _CHECK_LE2(lhs, rhs) _CHECK_LE3(lhs, rhs, "")
#define _CHECK_LT2(lhs, rhs) _CHECK_LT3(lhs, rhs, "")
#define _CHECK_GE2(lhs, rhs) _CHECK_GE3(lhs, rhs, "")
#define _CHECK_GT2(lhs, rhs) _CHECK_GT3(lhs, rhs, "")

#define _CHECK_EQ(...) OVERLOADED_MACRO(_CHECK_EQ, __VA_ARGS__)
#define _CHECK_NE(...) OVERLOADED_MACRO(_CHECK_NE, __VA_ARGS__)
#define _CHECK_LE(...) OVERLOADED_MACRO(_CHECK_LE, __VA_ARGS__)
#define _CHECK_LT(...) OVERLOADED_MACRO(_CHECK_LT, __VA_ARGS__)
#define _CHECK_GE(...) OVERLOADED_MACRO(_CHECK_GE, __VA_ARGS__)
#define _CHECK_GT(...) OVERLOADED_MACRO(_CHECK_GT, __VA_ARGS__)


static IP7_Client *pConsoleClient = P7_Create_Client(
    TM("/P7.Sink=Console /P7.Format=\"[%lv %fs:%fl @ %tm] %ms\" "));
static IP7_Trace *pConsoleTrace = P7_Create_Trace(pConsoleClient, TM("log"));

static IP7_Client *pFileTxtClient = P7_Create_Client(
    TM("/P7.Sink=FileTxt /P7.Format=\"[%lv %fs:%fl @ %tm] %ms\" /P7.Dir=D:/Logs/"));
static IP7_Trace *pFileTxtTrace = P7_Create_Trace(pFileTxtClient, TM("log"));


static IP7_Client *pCustomClient = nullptr;
static IP7_Trace *pCustomTrace = nullptr;

static void InitP7ConsoleTrace() {
  if ((pConsoleClient != nullptr) && (DEFAULT_TRACE != nullptr)) {
    LOG_INFO(TM("log compiled @ %s\n"), __TIMESTAMP__);
    printf("\b\n");
  } else {
    throw std::runtime_error("Init P7 console trace failed!");
  }
}

static void InitP7FileTxtTrace() {
  if ((pConsoleClient != nullptr) && (DEFAULT_TRACE != nullptr)) {
    LOG_INFO(TM("log compiled @ %s\n"), __TIMESTAMP__);
    printf("\b\n");
  } else {
    throw std::runtime_error("Init P7 console trace failed!");
  }
}
static void InitP7CustomTrace(
    const char *P7option = "/P7.Sink=Baical") {

  if (pCustomTrace) {
    pCustomTrace->Release();
    pCustomTrace = nullptr;
  }

  if (pCustomClient) {
    pCustomClient->Release();
    pCustomClient = nullptr;
  }

  pCustomClient = P7_Create_Client(P7option);
  pCustomTrace = P7_Create_Trace(pCustomClient, TM("log"));

  if (pCustomClient && pCustomTrace) {
    LOG_INFO(TM("P7 option %s"), P7option);
  } else {
    throw std::runtime_error("Init P7 custom trace failed!");
  }
}

#endif /* _LOG_LOG_H_ */
